#include "error.h"
#include "parse.h"
#include "symbol.h"

//#include "globals.h"
//#include "parse.h"
//#include "symtab.h"
//#include "operator.h"

/*

start
    : Declarations '%%' Grammar
    ;

Declarations
    : Declaration*
    ;
Declaration
    : TokenDecl \n
    | PrecDecl \n
    | Code  \n
    | \n
    ;
TokenDecl
    : '%token' (ident literal?)+ \n
    ;

PrecDecl
    : '%left'     opspec+ \n
    | '%right'    opspec+ \n
    | '%nonassoc' opspec+ \n
    ;

Grammar
    : ( IDENT ':' Multirule * ';' ? )*
    ;
Multirule
    : Rule ('|' Rule) * 

Rule
    : '(' Multirule ')'
    | Rule '*'
    | Rule '+'
    | Rule Rule
    | IDENT
    | 'literal'
    | <epsilon>
    ;

 */

/* TParseToken - Combine token and its index.
 *
 * During parsing, it is more convenient to have tokens that
 * include their index into the central token array. IOW, we
 * want an object that behaves just like a TToken but also
 * contains a TTokenIndex. Multiple inheritance makes this easy.
 */
struct TParseToken : TToken, TTokenIndex
    {
    TParseToken(const TToken& Token) : TToken(Token) {};
    TParseToken() {};
    };

class   TParser
    {
public:
    TParser(unique_ref<TTokens> Tokens_) : Tokens(Tokens_), SymbolTable(new TSymbolTable(Tokens_)) {}
    TParser& operator=(TParser& Other) =delete;
    void                ParseStart();
    void                ParseDeclarations();
    int                 ParseDeclaration();
    int                 ParseTokenDecl();
    void                ParsePrecDecl();
    void                ParseGrammar();
    void                ParseMultirule(TSymbol* LeftHandSide);
    TParseToken         GetNextToken();
    TParseToken         PeekNextToken();
    TParseToken         Match(int Type);
private:
    unique_ref<TTokens> Tokens;
    TParseToken         CurrentToken;
    TTokenIndex         TokenCursor = 0;
    bool                IgnoreNewlines = false;
    unique_ptr<TSymbolTable>    SymbolTable;
    };

/* ??? modify to handle includes */
TParseToken  TParser::GetNextToken()
    {
    CurrentToken    = Tokens->Get(TokenCursor++);
    while( CurrentToken.Type == TToken::WHITESPACE
        || CurrentToken.Type == TToken::COMMENT
        || CurrentToken.Type == TToken::MCOMMENT
        || (IgnoreNewlines && CurrentToken.Type == TToken::NEWLINE))
        CurrentToken = Tokens->Get(TokenCursor++);

    CurrentToken.Index  = TokenCursor-1;
    if(CurrentToken.Type == TToken::TKEOF)
        --TokenCursor;

    return CurrentToken;
    }

TParseToken  TParser::Match(int Type)
    {
    assert(Type == CurrentToken.Type);
    return  GetNextToken();
    }

TParseToken  TParser::PeekNextToken()
    {
    TParseToken  Result = CurrentToken;
    if(CurrentToken.Type != TToken::TKEOF)
        {
        TParseToken CurTok = CurrentToken;
        int         Cursor = TokenCursor;
        Result  = GetNextToken();
        CurrentToken    = CurTok;
        TokenCursor     = Cursor;
        }
    return Result;
    }


void TParser::ParseStart()
    {
    printf("ParseStart!\n");
    GetNextToken(); //??? why?
    ParseDeclarations();
    IgnoreNewlines = true;
    Match(TToken::SECTION);
    ParseGrammar();
    printf("Type=%d\n", CurrentToken.Type);
    if(CurrentToken.Type == TToken::SECTION)
        {
        Match(TToken::SECTION);
        if(CurrentToken.Type == TToken::PROGRAM)
            Match(TToken::PROGRAM);
        }

    printf("Type=%d\n", CurrentToken.Type);
    if(CurrentToken.Type != TToken::TKEOF)
        Error("SYNTAX_EXPECTING_EOF",
            "BadToken", TokenCursor-1
            );
    
    DUMPVERBOSE("%u nonterminals.\n", SymbolTable->NonTerminals.size());
    for(auto NonTerm : SymbolTable->NonTerminals)
        {
        DUMPVERBOSE("%s ->\n", NonTerm->Name().c_str());
        for(auto Rule : NonTerm->Derives)
            {
            DUMPVERBOSE("    %s -> ", Rule->Name().c_str());
            for(auto Item : Rule->Derives)
                {
                DUMPVERBOSE("%s ", Item->Name().c_str());
                }
            DUMPVERBOSE("\n");
            }
        }
    }

void TParser::ParseDeclarations()
    {
    printf("ParseDeclarations!\n");
    while(ParseDeclaration())
        ;
    printf("<ParseDeclarations returns\n");
    }
/*  PrecDecl
 *      : '%left'     opspec+ \n
 *      | '%right'    opspec+ \n
 *      | '%nonassoc' opspec+ \n
 *      ;
 */

void TParser::ParsePrecDecl()
    {
    printf("ParsePrecDecl!\n");
    GetNextToken();
    while(CurrentToken.Type == TToken::OPERATOR)
        GetNextToken();
    Match(TToken::NEWLINE);
    }

int  TParser::ParseDeclaration()
    {
    printf("ParseDeclaration!\n");
    int     Result  = 1;
    switch(CurrentToken.Type)
        {
        case    TToken::TOKEN :
            while(ParseTokenDecl())
                ;
            break;
        case    TToken::LEFT :
        case    TToken::RIGHT :
        case    TToken::NONASSOC :
            ParsePrecDecl();
            break;
        case TToken::CODE :
        case TToken::NEWLINE :
            Match(CurrentToken.Type);
            break;
        default:
            Result  = 0;
        }
    printf("ParseDeclaration returns %d: '%10.10s'\n", Result, CurrentToken.Text);
    return Result;
    }

/*  TokenDecl
 *      : '%token' (ident literal?)+ \n
 *      ;
 */
int    TParser::ParseTokenDecl()
    {
    DUMP("ParseTokenDecl()\n");

    Match(TToken::TOKEN);

    // while there are more token definitions
    while(CurrentToken.Type == TToken::IDENT)
        {
        auto    Symbol =  SymbolTable->Find(CurrentToken);
        if(Symbol)  // if symbol already defined
            {
            // if it was previously defined as a terminal
            if(Symbol->IsTerminal())
                Error("SYNTAX_TOKEN_ALREADY_DEFINED",
                    "SymRef",   CurrentToken.Index,
                    "SymDef",   Symbol->FirstUse()
                    );
            // else, previously defined as non-terminal (presumably via %start)
            else
                Error("SYNTAX_TERM_PREVDEF_NONTERM",
                    "SymRef",   CurrentToken.Index,
                    "SymDef",   Symbol->FirstUse()
                    );
            }
        Symbol  = SymbolTable->NewTerminal(CurrentToken.Index);

        GetNextToken();
        if(CurrentToken.Type == TToken::QUOTED)
            GetNextToken();
        }

    if(CurrentToken.Type == TToken::NEWLINE)
        GetNextToken();
    else
        Error("SYNTAX_EXPECTING_SOMETHING");

    DUMP("ParseTokenDecl() returns\n");

    return 0;
    }

void   TParser::ParseGrammar()
    {
    printf("ParseGrammar( '%10.10s'[%d])\n", CurrentToken.Text, CurrentToken.Type);
    printf("TokenCursor=%d\n", TokenCursor);
    auto  Next = PeekNextToken();

    printf("TokenCursor=%d\n", TokenCursor);
    // while we see another non-terminal
    while(CurrentToken.Type == TToken::IDENT && Next.Type == TToken::COLON)
        {
        TTokenIndex NontermTokenId  = CurrentToken.Index;

        auto LeftHandSide = SymbolTable->Find(CurrentToken);
        if(LeftHandSide && LeftHandSide->IsTerminal())
//        if(auto Terminal = SymbolTable->Find(CurrentToken))
            Error("SYNTAX_EXPECTING_NONTERM_NOT_TERM",
                "Colon",    TokenCursor+1, //??? wrong??
                "SymRef",   NontermTokenId,
                "SymDef",   LeftHandSide->FirstUse()
            );
        // if LHS was not already a defined symbol
        if(!LeftHandSide)
            LeftHandSide  = SymbolTable->NewNonterminal(CurrentToken.Index);

        Match(TToken::IDENT);   // eat the lhs nonterminal
        assert(CurrentToken.Type == TToken::COLON);
        Next    = PeekNextToken();
        if(Next.Type == TToken::DOTIDENT)
            Match(TToken::COLON);
        ParseMultirule(LeftHandSide);
        if(CurrentToken.Type == TToken::SEMICOLON)
            Match(TToken::SEMICOLON);
        Next    = PeekNextToken();
        }
    DUMP("ParseGrammar() returns\n");
    }

/* ParseMultiRule() - parse multiple rules of a single non-terminal.
 *
 *
 */
void    TParser::ParseMultirule(TSymbol* LeftHandSide)
    {
    DUMP("ParseMultirule(%s)\n", LeftHandSide->Name().c_str());
    // the lhs nonterminal and colon have already been eaten.
    
    do  {
        // compose name for new rule
        string RuleName = LeftHandSide->Name();
        if(CurrentToken.Type == TToken::DOTIDENT)
            RuleName   += CurrentToken;
        else
            RuleName   += "." + std::to_string(LeftHandSide->Derives.size());
        auto Rule = SymbolTable->NewRule(RuleName, CurrentToken.Index);

        LeftHandSide->AddSymbol(Rule, CurrentToken.Index);
        Match(CurrentToken.Type);   // match '|' or ':' or DOTIDENT

        while(CurrentToken.Type == TToken::IDENT || CurrentToken.Type == TToken::QUOTED)
            {
            if(CurrentToken.Type == TToken::IDENT)
                {
                auto Next    = PeekNextToken();
                if(Next.Type == TToken::COLON)
                    break;
                }
            Match(CurrentToken.Type);
            }
        } while(CurrentToken.Type == TToken::ORBAR || CurrentToken.Type == TToken::DOTIDENT);
    DUMP("ParseMultirule() returns\n");
    }


void Parse(unique_ref<TTokens> Tokens)
    {
    printf("Parse!\n");
    TParser Parser(Tokens);
    Parser.ParseStart();
    printf("Parse returns!\n");
    }

#if 0
TParse::TParser(unique_ptr<TTokens> Tokens_) : Tokens(std::move(Tokens_)) {};


TGrammar::TGrammar(unique_ref<TToken> Tokens_)
    : Tokens(Tokens_), TokenVector(*Tokens_)
    {
    MaxTokenId  = TokenVector.size();
    };

#endif
#if 0
/* SyntaxPuts() - helper function for SyntaxError()
 *
 * Our lovely text error messages may come out all misaligned
 * if the user has used tabs in a line that we're reporting in
 * an error message. The best we can do is ask the user to tell
 * us what tabstops they intended via the command-line '-t' option.
 *
 * Here, we print out a line, expanding any tab characters into
 * a (hopefully) appropriate number of spaces. We also report how
 * much we padded so that the caller can correctly point to the
 * relevant token on the line.
 */
static
int SyntaxPuts(const char* Str, int Len)
    {
    int     Padded  = 0;
    int     Column  = 0;

    while(Len-- > 0)
        {
        if(*Str == '\t')
            {
            --Padded;   /* first one doesn't count */
            do  {
                fprintf(stderr, " ");
                ++Column;
                ++Padded;
                } while(Column % 4);
            }
        else
            fprintf(stderr, "%c", *Str);
        ++Str;
        }
    fprintf(stderr, "\n");
    return Padded;
    }

#if 0
void    SyntaxError(int ErrorCode, TToken Token, const char* Format, ...)
    {
    va_list     ArgPtr;
    TContext    Context;
    int         LineNumber;
    char*       Buffer;
    const char* Severity = ErrorCode ? "Error" : "Warning";
    
    assert(Format != NULL);

    Buffer      = malloc(1024*8);
    assert(Buffer != NULL);
    
    Context     = InputGetContext(Token.Text);
    LineNumber  = Context.LineNumber;
    if(Context.LineNumber)
        fprintf(stderr, "Line %d: %s %d.\n", Context.LineNumber, Severity, ErrorCode);
    else
        fprintf(stderr, "%s %d.\n", Severity, ErrorCode);

    if(Context.LineLen == 0)
        fprintf(stderr, "<EOF>\n");
    else
        {
        int     Padded;
        size_t  VisualLen, iDash;
        
        Padded = SyntaxPuts(Context.LineStart, Context.LineLen);
//        fprintf(stderr, "%.*s\n", Context.LineLen, Context.LineStart);
        fprintf(stderr, "%*.0s^", (Token.Text - Context.LineStart) + Padded, " ");
        for(VisualLen=0; VisualLen < Token.TextLen; ++VisualLen)
            if(Token.Text[VisualLen] == '\r' || Token.Text[VisualLen] == '\n')
                break;
        for(iDash = 1; iDash < VisualLen-1; ++iDash)
            fprintf(stderr, "-");
        if(Token.TextLen > 1)
            fprintf(stderr, "^");
        fprintf(stderr, "\n");
        }

    assert(Format != NULL);
    va_start(ArgPtr, Format);
    vsprintf(Buffer, Format, ArgPtr);
    va_end(ArgPtr);

    if(!StrEndsWith(Buffer, "\n"))
        strcat(Buffer, "\n");
    fputs(Buffer, stderr);

    if(LineNumber == 0)
        LineNumber  = -1;
    if(ErrorCode)
        Exit(LineNumber, ErrorCode);
    }
#endif

/* AcceptLhs() - accept the left-hand side of a production.
 */
static
TSymbol*   AcceptLhs(TToken Token)
    {
    TSymbol*    Symbol;

    Symbol  = SymbolFind(Token);
    if(Symbol)  /* if this symbol already seen */
        {
        if(SymbolGetBit(Symbol, SYM_TERMINAL))
            {
            SyntaxError(ERROR_TERM_USED_AS_NONTERM, Token,
                        "Expecting a non-terminal to start a production, "
                        "but '%.*s' was previously defined as a terminal.\n",
                        Symbol->Name.TextLen, Symbol->Name.Text);
            }
        else
            {
            assert(SymbolGetBit(Symbol, SYM_TERMINAL) == FALSE);
            if(Symbol->Lhs.Text && Globals.Verbose)
                {
                /* ??? report line previously defined on! */
                SyntaxError(ERROR_NONE, Token,
                            "Non-terminal previously defined. Legal, but mildly unusual.\n");
                }
            }
        }
    else
        {
        Symbol      = SymbolNewNonTerm(Token);
        Symbol->Lhs = Token;
        }

    return Symbol;
    }

/* ???TODO: define/handle a complete literal syntax here... */
static
int     IsCharLiteral(TToken Token)
    {
    int     Result  = -1;
    
    if(Token.TextLen >= 3 && Token.Text[0] == '\'' && Token.Text[Token.TextLen-1] == '\'')
        {
        if(Token.TextLen == 3)
            Result  = Token.Text[1];
        else if(Token.TextLen == 4 && Token.Text[1] == '\\')
            {
            switch(Token.Text[2])
                {
                case    'b' : Result    = '\b'; break;
                case    'r' : Result    = '\r'; break;
                case    't' : Result    = '\t'; break;
                case    '\'': Result    = '\''; break;
                case    '\\': Result    = '\\'; break;
                case    'n' : Result    = '\n'; break;
                case    'f' : Result    = '\f'; break;
                }
            }
        }

    return Result;
    }

/* AcceptRhs() - accept a right-hand-side production item.
 *
 * On the right-hand side of a production, we just found either an
 * identifier or a quoted string. If it's an identifier, then it's
 * either a symbolic name for a terminal, or a non-terminal. If it's
 * a quoted string, then it's an unnamed terminal.
 *
 * We return the TSymbol associated with Token (created or found).
 */
static
TSymbol* AcceptRhs(TToken LhsToken, TToken Token, TRule* Rule)
    {
    TSymbol*    Symbol      = NULL;

    if(Token.Type == TK_IDENT || Token.Type == TK_QUOTED)
        {
        Symbol = SymbolFind(Token);

        if(Symbol == NULL)  /* if unknown identifier, presume non-terminal */
            {
            if(Token.Type == TK_IDENT)
                Symbol      = SymbolNewNonTerm(Token);
            else if(Token.Type == TK_QUOTED)
                {
                int     Value = IsCharLiteral(Token);
                
                if(Value >= 0)
                    {
                    Symbol          = SymbolNewTerm(Token);
                    SymbolSetValue(Symbol, Value);
                    }
                else
                    {
                    Symbol  = LiteralFind(Token);
                    if(Symbol == NULL)
                        {
                        TToken  Clean = Unquote(Token);
                        SyntaxError(ERROR_UNDECL_LITERAL_IN_PROD, Token,
                                    "Literal '%.*s' in production of '%.*s' should be "
                                    "defined with a %%token declaration.\n",
                                    Clean.TextLen, Clean.Text,
                                    LhsToken.TextLen, LhsToken.Text);
                        }
                    }
                }
            else
                assert(FALSE);
            }
        }
    else
        assert(FALSE);

    RuleAddSymbol(Rule, Symbol, Token);

    Symbol->UsedInRule  = TRUE;

    return Symbol;
    }


/* ParseToken() - parse a token declaration.
 *
 * %token (ident literal?)+ \n
 */
static
void    ParseToken(TToken Token)
    {
    TSymbol*    NewTerminal = NULL;

    Token       = InputPeekNext();
    if(Token.Type != TK_IDENT && Token.Type != TK_QUOTED)
        SyntaxError(ERROR_MISSING_NAME_IN_TOKEN_DECL, Token,
                    "Expecting identifier after '%token'.\n");
    while(Token.Type == TK_QUOTED || Token.Type == TK_IDENT)
        {
        if(Token.Type == TK_QUOTED && NewTerminal == NULL)
            SyntaxError(ERROR_MISSING_NAME_IN_TOKEN_DECL, Token,
                        "Missing token name in front of quoted string.\n");

        if(Token.Type == TK_IDENT)
            {
            NewTerminal = SymbolFind(Token);
            if(NewTerminal)
                {
                SyntaxError(ERROR_TERM_ALREADY_DEFINED, Token,
                            "'%.*s' was previously defined.\n",
                            Token.TextLen, Token.Text);
                }
            else
                NewTerminal = SymbolNewTerm(Token);
            }
        else if(Token.Type == TK_QUOTED)
            {
            TSymbol*    PreviousDef;

            PreviousDef = LiteralFind(Token);
//??? Does this really work? Are we guaranteed that context of a token name will
//??? be the line it was first defined on?
            if(PreviousDef)
                {
                TContext    Previous;

                Previous = InputGetContext(PreviousDef->Name.Text);
                assert(Previous.LineNumber != 0);

                SyntaxError(ERROR_LITERAL_ALREADY_DEFINED, Token,
                            "This literal was previously assigned a token name of '%.*s' on line %d:\n%.*s\n",
                            PreviousDef->Name.TextLen, PreviousDef->Name.Text,
                            Previous.LineNumber, Previous.LineLen, Previous.LineStart);
                }
            LiteralAdd(NewTerminal, Token);
            NewTerminal = NULL;
            }

        InputGetNext();    /* eat token we already peeked at */
        Token   = InputPeekNext();
        }
    }
/* ParseStart() - parse declaration of starting non-terminal
 *
 * %token ident
 */
static
void    ParseStart(TToken Token)
    {
    
    Token       = InputPeekNext();
    if(Token.Type != TK_IDENT)
        {
        SyntaxError(ERROR_EXPECTING_IDENT_AFTER_START, Token,
                    "Expecting a non-terminal name after %%start!\n");
        }
    else
        {
        TSymbol*    Symbol = SymbolNewNonTerm(Token);

        InputGetNext();
        SymbolAddStart(Symbol);
        /* ??? check for already a start symbol! */
        }

    /* ??? Not done - must do something with start decl!!! */
    }


/* ParseOperand() - parse operand declaration
 *
 * %operand ident*
 */
static
void    ParseOperand(TToken Token)
    {
    TSymbol*    NewTerminal = NULL;

    Token       = InputPeekNext();
    while(Token.Type == TK_IDENT)
        {
        NewTerminal = SymbolFind(Token);
        if(NewTerminal == NULL)
            NewTerminal = SymbolNewTerm(Token);
        NewTerminal->Operand    = TRUE;
        InputGetNext();    /* eat token we already peeked at */
        Token   = InputPeekNext();
        }
    }


/* OperatorToSymbol() - from a token representing an operator, produce a TSymbol
 *
 * An operator declaration might look like this:
 *     %left X++
 * When we encounter the '++', if it's not already been defined as
 * a token literal like this:
 *     %token TK_SOMETHING_OR_OTHER '++'
 * we will look up the corresponding token. We will also implicitly
 * create the token if necessary if it's a single-character literal.
 *    Finally, if it's a multi-character literal and not already defined,
 * then that's a syntax error.
 */
static
TSymbol*    OperatorToSymbol(TToken Token)
    {
    TSymbol*    Result;

    /* maybe it's an already defined literal */
    Result  = LiteralFind(Token);
    if(Result == NULL && Token.TextLen == 1)  /* if it's not a defined literal */
        {
        TToken  OpToken = TokenFake(TK_QUOTED, "'%.*s'", Token.TextLen, Token.Text);

        Result          = SymbolFind(OpToken);
        if(Result == NULL)
            {
            Result  = SymbolNewTerm(OpToken);
            SymbolSetValue(Result, Token.Text[0]);
            }
        /* else, we wasted a few bytes of memory that won't get
         * freed up until InputDestroy() gets called.
         */
        }

    return Result;
    }

/* OperatorConflict() - Check for conflicts in operator declarations
 *
 * Operator homonyms allowed (once):
 *  - .X and X.
 *  - .X and X.X
 *  - .X., X.X. and .X.X
 */
static
int         OperatorConflict(TOperator* Old, TOperator* New)
    {
    SYMBOLS     OldList, NewList;
    int         NOld, NNew, iOld, iNew;
    int         Dup;
    TSymbol*    OldSym;
    TSymbol*    NewSym;

    OldList = Old->List;
    NOld    = SymbolListCount(OldList);
    NewList = New->List;
    NNew    = SymbolListCount(NewList);

    Dup     = 0;
    for(iOld = 0; iOld < NOld && !Dup; ++iOld)
        {
        OldSym  = SymbolListGet(OldList, iOld);
        if(OldSym)  /* NULL means operand placeholder */
            {
            for(iNew = 0; iNew < NNew; ++iNew)
                {
                NewSym  = SymbolListGet(NewList, iNew);

                if(NewSym == OldSym)
                    {
                    Dup     = TRUE;
                    break;
                    }
                }
            }
        }
    return Dup;
    }

/* ParseOperator() - parse an operator pattern from an operator precedence decl
 */
static
void  ParseOperator(int Precedence, int Associativity, TToken Token, int MarkChar)
    {
    int         NSymbols, NOperators, iOperator;
    SYMBOLS     List            = NULL;
    const char* Sentinel        = Token.Text + Token.TextLen;
    const char* Rover           = Token.Text;
    char        Pattern[MAX_OPERATOR_PATTERN+1];
    int         IsQuoted        = FALSE;
    int         OperandCount    = 0;

    if(*Rover == '\'' || *Rover == '"')
        if(Token.TextLen > 1 && *Rover == Token.Text[Token.TextLen-1])
            IsQuoted    = TRUE;
    Pattern[0]  = '\0';
    for(NSymbols = 0; Rover < Sentinel; ++NSymbols)
        {
        if(*Rover == MarkChar)  /* if it's an operand placeholder */
            {
            List    = SymbolListAdd(List, NULL);
            strcat(Pattern, "X");
            ++Rover;
            ++OperandCount;
            }
        else
            {
            TToken      Operator;
            TSymbol*    Symbol;
            char        Buffer[128];

            Operator.Text   = Rover;
            while(Rover < Sentinel && *Rover != MarkChar)
                ++Rover;
            Operator.TextLen    = Rover - Operator.Text;
            Operator.Type       = TK_OPERATOR;

            Symbol  = OperatorToSymbol(Operator);
            if(Symbol == NULL)
                {
                strcpy(Buffer, "This operator was not defined with a %%token declaration.");
                if(Operator.Text[0] == '/')
                    strcat(Buffer, "\n(Note that comments are not allowed in a precedence declaration)\n");
                else if(IsQuoted)
                    strcat(Buffer, "\n(Note that you should not quote operator patterns)\n");
                SyntaxError(ERROR_UNDEFINED_OPERATOR, Operator, Buffer);
                }
            if(SymbolListContains(List, Symbol))
                {
                SyntaxError(ERROR_DUP_SYM_IN_OPERATOR, Operator,
                            "Same symbol used twice in one operator.\n");
                }
            List    = SymbolListAdd(List, Symbol);
            strcat(Pattern, ".");
            }
        if(strlen(Pattern) >= MAX_OPERATOR_PATTERN)
            {
            SyntaxError(ERROR_OP_DECL_TOO_LONG, Token,
                        "This operator declaration is too darned long."
                        );
            }
        }
    if(OperandCount <= 0)
        SyntaxError(ERROR_MISSING_OPERAND_IN_DECL, Token,
            "Missing operand in operator declaration (operands are denoted by an 'X').\n");
    else if(!PatternCmp("^.*X$", Pattern) && Associativity == TK_LEFT)
        SyntaxError(ERROR_PREFIX_UNARY_DECL_LEFT, Token,
                    "Prefix unary operators cannot be declared left-associative.\n");
    else if(!PatternCmp("X*.$", Pattern) && Associativity == TK_RIGHT)
        SyntaxError(ERROR_POSTFIX_UNARY_DECL_RIGHT, Token,
                    "Postfix unary operators cannot be declared right-associative.\n");
    else if(!PatternCmp("^.*.$", Pattern) && Associativity != TK_NONASSOC)
        SyntaxError(ERROR_MUST_BE_NONASSOC, Token,
                    "Bracketing operators must be declared non-associative.\n");
    OperatorAdd(Token, Precedence, Associativity, List, Pattern);

    /* see if this conflicts with previous operator declarations */
    NOperators = OperatorCount();
    for(iOperator = 0; iOperator < NOperators-1; ++iOperator)
        {
#if 0
        TOperator*  PrevOp    = OperatorGet(iOperator);
        
/* ???TODO */
        if(OperatorConflict(PrevOp, NewOp))
            SyntaxError(ERROR_DUPL_OP_DECL, Token,
                        "This declaration conflicts with previous declaration of: '%.*s'\n",
                        PrevOp->Decl.TextLen, PrevOp->Decl.Text);
#endif
        }
    }


/* ParsePrecedence() - parse a precedence declaration.
 *
 * %{left|right|nonassoc} (ident literal?)+
 */
static
void    ParsePrecedence(TToken Direction)
    {
    static int  Precedence      = 1;    /* precedence 0 reserved for $, so start at 1 */
    int         Associativity   = Direction.Type;
    int         OperatorCount   = 0;
    TToken      Token;

    for(;;)
        {
        Token   = InputGetNext();
        if(Token.Type == TK_EOF)
            SyntaxError(ERROR_EOF_IN_PREC, Token,
                        "Unexpected EOF encountered while processing precedence directive.\n");
        else if(Token.Type == TK_NEWLINE)
            break;
        else if(Token.Type == TK_OPERATOR)
            {
            ParseOperator(Precedence, Associativity, Token, 'X');
            ++OperatorCount;
            }
        /* can't really happen, but defensive programming */
        else
            SyntaxError(ERROR_EXPECTING_OPERATOR_DECL, Token,
                        "Illegal token; expecting an operator declaration.\n");
        }

    if(OperatorCount == 0)
        SyntaxError(ERROR_PREC_WITHOUT_OPERATORS, Direction,
                    "Precedence directive line contained no operators!\n");
    ++Precedence;
    }

/* SkipTest() - skip over a %test declaration.
 *
 * We don't do anything with test data during the initial parsing phase,
 * so this function just skips over a %test declaration, assuming it is
 * syntactically correct.
 */
static
void    SkipTest(TToken TestStart)
    {
    TSymbol*    Symbol;
    TToken      Token;

//    Token   = InputGetLine();
//    assert(Token.Type == TK_LINE);
    Token   = InputGetNext();
    while(Token.Type != TK_TEST)
        {
        switch(Token.Type)
            {
            case    TK_EOF  :
                SyntaxError(ERROR_EOF_IN_TEST, TestStart,
                            "Found no terminating '%test' for this test.");
                break;
            case    TK_IDENT    :
            case    TK_QUOTED   :
                if(Token.Type == TK_IDENT)
                    Symbol  = SymbolFind(Token);
                else
                    Symbol  = LiteralFind(Token);
                if(!Symbol)
                    SyntaxError(ERROR_UNDEF_SYMBOL_IN_TEST, Token,
                                "Undefined symbol inside %%test.");
                else if(SymbolGetBit(Symbol, SYM_TERMINAL) == FALSE)
                    SyntaxError(ERROR_NONTERM_IN_TEST, Token,
                                "Only terminal symbols allowed in %%test data.");
                break;
            default:
                SyntaxError(ERROR_BAD_TOKEN_IN_TEST, Token,
                            "Expecting terminal (literal or symbolic) inside %%test.");
            }
        Token   = InputGetNext();
        }
    }

static
void    ParseDeclarations()
    {
    TToken      Token;

    Dump("ParseDeclarations()\n");
    Token   = InputGetNext();
    while(Token.Type != TK_SECTION)
        {
        if(Token.Type == TK_LEFT || Token.Type == TK_RIGHT || Token.Type == TK_NONASSOC)
            ParsePrecedence(Token);
        else if(Token.Type == TK_TOKEN)
            ParseToken(Token);
        else if(Token.Type == TK_OPERAND)
            ParseOperand(Token);
        else if(Token.Type == TK_TEST)
            SkipTest(Token);
        else if(Token.Type == TK_START)
            ParseStart(Token);
        else if(Token.Type == TK_CODE)
            ;/*???*/
        else if(Token.Type == TK_EOF)
            SyntaxError(ERROR_EOF_IN_PREC, Token,
                        "Unexpected EOF in declaration section.\n", Token.Type);
        else
            SyntaxError(ERROR_BAD_TOKEN_IN_DECL, Token,
                        "Unexpected token [%d] in declaration section.\n", Token.Type);
        Token   = InputGetNext();
        }
#if 0
#endif
    Dump("ParseDeclarations() returns\n");
    }


/* FakeActionToken() - create a fake token that names an action.
 *
 * No matter where an action occurs, we create a new null non-terminal
 * for it. This lets us treat embedded actions in rules more or less uniformly
 * ("normal" actions just happen to occur at the end of the rule).
 */
static
TToken FakeActionToken()
    {
    int         NActions;
    char        NameBuffer[32];
    char*       Name;
    TToken      Fake;

    /* create fake name based on action # */
    NActions    = SymbolListCount(Globals.Actions);
    sprintf(NameBuffer, "{A%d}", NActions+1);
#if 0
    Action      = NEW(TAction);
    assert(Action != NULL);
    Action->Number  = NActions+1;
    Action->Action  = ActionToken;
#endif
    Fake.Type    = TK_IDENT;
    Fake.TextLen = strlen(NameBuffer);
    Name                = malloc(Fake.TextLen+1);
    strcpy(Name, NameBuffer);
    Fake.Text    = Name;
#if 0
    Result          = SymbolNewNonTerm(NonTermName);
    Result->Action  = Action;
    Globals.Actions = SymbolListAdd(Globals.Actions, Result);
#endif
    return Fake;
    }

#if 0
static
void    CheckRuleDup(TSymbol* Lhs, TRule* Rule)
    {
    int     iRule, NRules;
    TRule*  NewRule;

    NRules  = Lhs->NRules;
#if 0
    for(iRule = 0; iRule < NRules-1; ++iRule)
        {
        int     iItem, jItem, NActions;
        TRule*  OldRule = Lhs->Rules[iRule];

        iItem = jItem = NActions = 0;
        while(iItem < 
        }
#endif
    }

#endif

/* HandleAction() - handle a TK_ACTION token in a rule.
 *
 * There is a default action that gets reused for any rule that
 * has no trailing action.
 */
static
void    HandleAction(TToken LhsToken, TToken ActionToken, TRule* Rule)
    {
    int         IsDefault;
    TSymbol*    Symbol;
    TToken      ActionName;

    IsDefault   = TokenEqual(ActionToken, DefActToken);

    if(IsDefault)
        ActionName  = ActionToken;
    else
        ActionName  = FakeActionToken();

    /* turn action into a hidden non-terminal with an empty rule */
    Symbol  = AcceptRhs(LhsToken, ActionName, Rule);
    assert(SymbolGetBit(Symbol, SYM_TERMINAL) == FALSE);

    if(Symbol->Action)
        assert(IsDefault);
    else
        {
        Symbol->Action  = NEW(TAction);
        assert(Symbol->Action != NULL);

        Symbol->Action->Action  = ActionToken;
        Globals.Actions  = SymbolListAdd(Globals.Actions, Symbol);
        Symbol->Action->Number = SymbolListCount(Globals.Actions);
        /* do not assign Action->ArgCount here, grammar rewriting may change it! */
        /* nonterminal representing action gets a null rule */
        SymbolNewRule(Symbol);
        }
    }

#endif

#if 0
int TGrammar::GetNextToken()
    {
    Token   = TokenVector[TokenId];
    
    }

/* ParseInput() - parse input text, create data structures
 */
void    TGrammar::ParseTokens()
    {
    TToken  Token = TToken::Null;
    TToken  LhsToken;

    ParseDeclarations();

    /* for each production (including alternatives) */
    while(Token.Type != TToken::TKEOF)
        {
        TSymbol*    LHS;
        TRule*      Rule;
        int         iSymbol;
        int         LastTokenWasAction = FALSE;
        
        if(Token.Type != TK_NONTERM)
            Token   = InputGetNext();
        if(Token.Type == TK_EOF || Token.Type == TK_SECTION)
            break;
        else if(Token.Type != TK_NONTERM && Token.Type != TK_IDENT)
            SyntaxError(ERROR_EXPECTING_NONTERM, Token,
                        "Expecting a non-terminal to start a production.");
        LHS     = AcceptLhs(Token);
        if(SymbolStart() == NULL)
            SymbolAddStart(LHS);

        LhsToken    = Token;
        Token       = InputGetNext();
        if(Token.Type != ':')
            SyntaxError(ERROR_EXPECTING_COLON, Token,
                        "Expecting ':' after non-terminal.");
        if(Token.Type == '^')
            {
            LHS->Operand            = TRUE;
            LHS->OperatorTrigger    = TRUE;
            }

        /* guarantee a rule, even if it's null */
        Rule    = SymbolNewRule(LHS);
        /* for each alternative (including action) */
        while(Token.Type != ';' && Token.Type != TK_NONTERM)
            {
            TSymbol*    Symbol = NULL;

            Token   = InputGetNext();
            LastTokenWasAction  = (Token.Type == TK_ACTION);

            /* while not at end-of-rule */
            for(iSymbol=0; Token.Type != ';' && Token.Type != '|'; ++iSymbol)
                {
                /* if it's a terminal or non-terminal */
                if(Token.Type == TK_IDENT || Token.Type == TK_QUOTED)
                    Symbol = AcceptRhs(LhsToken, Token, Rule);
                /* if it's the lookahead operator (syntactic predicate) */
                else if(Token.Type == '/')
                    {
                    if(iSymbol == 0)
                        SyntaxError(ERROR_NO_SYMBOL_BEFORE_SLASH, Token,
                            "In production for '%.*s': '/' must come after some symbol.");
                    /* ???TODO: check for multiple predicates even of different types */
                    assert(Symbol != NULL);
                    Symbol->PredicateEnd    = TRUE;
                    Rule->Predicate = TRUE;
                    }
                else if(Token.Type == TK_ACTION)
                    HandleAction(LhsToken, FakeActionToken(), Rule);
                else
                    SyntaxError(ERROR_EXPECTING_OR_SEMI, Token,
                                "In production for '%.*s': expecting non-terminal, "
                                "action, '|', '/', or ';'\n", LHS->Name.TextLen, LHS->Name.Text);
                Token   = InputGetNext();
                }
            if(!LastTokenWasAction)
                HandleAction(LhsToken, DefActToken, Rule);

            if(Token.Type == '|')
                Rule    = SymbolNewRule(LHS);
            }

        if(Token.Type != ';')
            SyntaxError(ERROR_EXPECTING_SEMICOLON, Token,
                        "Expecting ';' to terminate production list.");
        }
    if(Token.Type == TK_SECTION)
        Token   = InputGetNext();

    if(Token.Type == TK_PROGRAM)
        Globals.ProgramSection  = Token;
    else if(Token.Type == TK_EOF)
        ;
    else
        assert(FALSE);
    }

#endif
